home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / lightning-0.8-tb-win.xpi / chrome / calendar.jar / content / calendar / calendar-views.js < prev    next >
Text File  |  2008-02-24  |  25KB  |  679 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Calendar views code
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  *   the Mozilla Calendar Squad
  18.  * Portions created by the Initial Developer are Copyright (C) 2006
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
  23.  *   Joey Minta <jminta@gmail.com>
  24.  *   Michael Buettner <michael.buettner@sun.com>
  25.  *   gekacheka@yahoo.com
  26.  *   Matthew Willis <lilmatt@mozilla.com>
  27.  *   Philipp Kewisch <mozilla@kewis.ch>
  28.  *   Martin Schroeder <mschroeder@mozilla.x-home.org>
  29.  *
  30.  * Alternatively, the contents of this file may be used under the terms of
  31.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  32.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33.  * in which case the provisions of the GPL or the LGPL are applicable instead
  34.  * of those above. If you wish to allow use of your version of this file only
  35.  * under the terms of either the GPL or the LGPL, and not to allow others to
  36.  * use your version of this file under the terms of the MPL, indicate your
  37.  * decision by deleting the provisions above and replace them with the notice
  38.  * and other provisions required by the GPL or the LGPL. If you do not delete
  39.  * the provisions above, a recipient may use your version of this file under
  40.  * the terms of any one of the MPL, the GPL or the LGPL.
  41.  *
  42.  * ***** END LICENSE BLOCK ***** */
  43.  
  44. var calendarViewController = {
  45.     QueryInterface: function(aIID) {
  46.         if (!aIID.equals(Components.interfaces.calICalendarViewController) &&
  47.             !aIID.equals(Components.interfaces.nsISupports)) {
  48.             throw Components.results.NS_ERROR_NO_INTERFACE;
  49.         }
  50.  
  51.         return this;
  52.     },
  53.  
  54.     createNewEvent: function (aCalendar, aStartTime, aEndTime) {
  55.         aCalendar = aCalendar || getSelectedCalendar();
  56.  
  57.         // if we're given both times, skip the dialog
  58.         if (aStartTime && aEndTime && !aStartTime.isDate && !aEndTime.isDate) {
  59.             var event = createEvent();
  60.             event.startDate = aStartTime;
  61.             event.endDate = aEndTime;
  62.             var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
  63.                                 .getService(Components.interfaces.nsIStringBundleService);
  64.             var props = sbs.createBundle("chrome://calendar/locale/calendar.properties");
  65.             event.title = props.GetStringFromName("newEvent");
  66.             setDefaultAlarmValues(event);
  67.             doTransaction('add', event, aCalendar, null, null);
  68.         } else if (aStartTime && aStartTime.isDate) {
  69.             var event = createEvent();
  70.             event.startDate = aStartTime;
  71.             setDefaultAlarmValues(event);
  72.             doTransaction('add', event, aCalendar, null, null);
  73.         } else {
  74.             createEventWithDialog(aCalendar, aStartTime, null);
  75.         }
  76.     },
  77.  
  78.     pendingJobs: [],
  79.  
  80.     // in order to initiate a modification for the occurrence passed as argument
  81.     // we create an object that records the necessary details and store it in an
  82.     // internal array ('pendingJobs'). this way we're in a position to terminate
  83.     // any pending modification if need should be.
  84.     createPendingModification: function (aOccurrence) {
  85.         // finalize a (possibly) pending modification. this will notify
  86.         // an open dialog to save any outstanding modifications.
  87.         aOccurrence = this.finalizePendingModification(aOccurrence);
  88.  
  89.         var pendingModification = {
  90.             controller: this,
  91.             item: aOccurrence,
  92.             finalize: null,
  93.             dispose: function() {
  94.                 var array = this.controller.pendingJobs;
  95.                 for (var i=0; i<array.length; i++) {
  96.                     if (array[i] == this) {
  97.                         array.splice(i,1);
  98.                         break;
  99.                     }
  100.                 }
  101.             }
  102.         }
  103.  
  104.         this.pendingJobs.push(pendingModification);
  105.  
  106.         modifyEventWithDialog(aOccurrence,pendingModification);
  107.     },
  108.  
  109.     // iterate the list of pending modifications and see if the occurrence
  110.     // passed as argument is currently about to be modified (event dialog is
  111.     // open with the item in question). if this should be the case we call
  112.     // finalize() in order to bring the dialog down and avoid dataloss.
  113.     finalizePendingModification: function (aOccurrence) {
  114.  
  115.       for each (var job in this.pendingJobs) {
  116.           var item = job.item;
  117.           var parent = item.parent;
  118.           if ((item.hashId == aOccurrence.hashId) ||
  119.               (item.parentItem.hashId == aOccurrence.hashId) ||
  120.               (item.hashId == aOccurrence.parentItem.hashId)) {
  121.               // terminate() will most probably create a modified item instance.
  122.               aOccurrence = job.finalize();
  123.               break;
  124.         }
  125.       }
  126.  
  127.       return aOccurrence;
  128.     },
  129.  
  130.     modifyOccurrence: function (aOccurrence, aNewStartTime, aNewEndTime, aNewTitle) {
  131.  
  132.         aOccurrence = this.finalizePendingModification(aOccurrence);
  133.  
  134.         // if modifying this item directly (e.g. just dragged to new time),
  135.         // then do so; otherwise pop up the dialog
  136.         if (aNewStartTime || aNewEndTime || aNewTitle) {
  137.             var instance = aOccurrence.clone();
  138.  
  139.             if (aNewTitle) {
  140.                 instance.title = aNewTitle;
  141.             }
  142.  
  143.             // When we made the executive decision (in bug 352862) that
  144.             // dragging an occurrence of a recurring event would _only_ act
  145.             // upon _that_ occurrence, we removed a bunch of code from this
  146.             // function. If we ever revert that decision, check CVS history
  147.             // here to get that code back.
  148.  
  149.             if (aNewStartTime || aNewEndTime) {
  150.                 // Yay for variable names that make this next line look silly
  151.                 if (instance instanceof Components.interfaces.calIEvent) {
  152.                     if (aNewStartTime && instance.startDate) {
  153.                         instance.startDate = aNewStartTime;
  154.                     }
  155.                     if (aNewEndTime && instance.endDate) {
  156.                         instance.endDate = aNewEndTime;
  157.                     }
  158.                 } else {
  159.                     if (aNewStartTime && instance.entryDate) {
  160.                         instance.entryDate = aNewStartTime;
  161.                     }
  162.                     if (aNewEndTime && instance.dueDate) {
  163.                         instance.dueDate = aNewEndTime;
  164.                     }
  165.                 }
  166.             }
  167.  
  168.             // If the item contains attendees then they need to be notified
  169.             if (instance.hasProperty("X-MOZ-SEND-INVITATIONS") &&
  170.                (instance.getProperty("X-MOZ-SEND-INVITATIONS") == "TRUE")) {
  171.                sendItipInvitation(instance);
  172.             }
  173.  
  174.             doTransaction('modify', instance, instance.calendar, aOccurrence, null);
  175.         } else {
  176.             // prompt for choice between occurrence and master for recurrent items
  177.             var itemToEdit = getOccurrenceOrParent(aOccurrence);
  178.             if (!itemToEdit) {
  179.                 return;  // user cancelled
  180.             }
  181.  
  182.             this.createPendingModification(itemToEdit);
  183.         }
  184.     },
  185.  
  186.     deleteOccurrences: function (aCount,
  187.                                  aOccurrences,
  188.                                  aUseParentItems,
  189.                                  aDoNotConfirm) {
  190.         startBatchTransaction();
  191.         var recurringItems = {};
  192.  
  193.         function getSavedItem(aItemToDelete) {
  194.             // Get the parent item, saving it in our recurringItems object for
  195.             // later use.
  196.             var hashVal = aItemToDelete.parentItem.hashId;
  197.             if (!recurringItems[hashVal]) {
  198.                 recurringItems[hashVal] = {
  199.                     oldItem: aItemToDelete.parentItem,
  200.                     newItem: aItemToDelete.parentItem.clone()
  201.                 };
  202.             }
  203.             return recurringItems[hashVal];
  204.         }
  205.  
  206.         // Make sure we are modifying a copy of aOccurrences, otherwise we will
  207.         // run into race conditions when the view's doDeleteItem removes the
  208.         // array elements while we are iterating through them.
  209.         var occurrences = aOccurrences.slice(0);
  210.  
  211.         for each (var itemToDelete in occurrences) {
  212.             if (aUseParentItems) {
  213.                 // Usually happens when ctrl-click is used. In that case we
  214.                 // don't need to ask the user if he wants to delete an
  215.                 // occurrence or not.
  216.                 itemToDelete = itemToDelete.parentItem;
  217.             } else if (!aDoNotConfirm && occurrences.length == 1) {
  218.                 // Only give the user the selection if only one occurrence is
  219.                 // selected. Otherwise he will get a dialog for each occurrence
  220.                 // he deletes.
  221.                 itemToDelete = getOccurrenceOrParent(itemToDelete);
  222.             }
  223.             if (!itemToDelete) {
  224.                 continue;
  225.             }
  226.             itemToDelete = this.finalizePendingModification(itemToDelete);
  227.             if (itemToDelete.parentItem.hashId != itemToDelete.hashId) {
  228.                 var savedItem = getSavedItem(itemToDelete);
  229.                 savedItem.newItem.recurrenceInfo
  230.                          .removeOccurrenceAt(itemToDelete.recurrenceId);
  231.                 // Dont start the transaction yet. Do so later, in case the
  232.                 // parent item gets modified more than once.
  233.             } else {
  234.                 doTransaction('delete', itemToDelete, itemToDelete.calendar, null, null);
  235.             }
  236.         }
  237.  
  238.         // Now handle recurring events. This makes sure that all occurrences
  239.         // that have been passed are deleted.
  240.         for each (var ritem in recurringItems) {
  241.             doTransaction('modify',
  242.                           ritem.newItem,
  243.                           ritem.newItem.calendar,
  244.                           ritem.oldItem,
  245.                           null);
  246.         }
  247.         endBatchTransaction();
  248.     }
  249. };
  250.  
  251. /**
  252.  * This function provides a neutral way to switch between views.
  253.  * XXX Kind of confusing. This function calls the app specific function, which
  254.  * again calls the common switchToView function. They should be consolidated in
  255.  * a different bug.
  256.  */
  257. function showCalendarView(type) {
  258.     if (isSunbird()) {
  259.         gCalendarWindow.switchToView(type);
  260.     } else {
  261.         ltnShowCalendarView(type);
  262.     }
  263. }
  264.  
  265. /**
  266.  * This function acts like the above, but does not bring the view to the front
  267.  * if the application is showing other elements (i.e Lightning).
  268.  */
  269. function selectCalendarView(type) {
  270.     if (isSunbird()) {
  271.         gCalendarWindow.switchToView(type);
  272.     } else {
  273.         ltnSelectCalendarView(type);
  274.     }
  275. }
  276.  
  277. /**
  278.  * This function does the common steps to switch between views. Should be called
  279.  * from app-specific view switching functions
  280.  */
  281. function switchToView(aViewType) {
  282.     var viewDeck = getViewDeck();
  283.     var selectedDay;
  284.     var currentSelection = [];
  285.  
  286.     // Set up the view commands
  287.     var views = viewDeck.childNodes;
  288.     for (var i = 0; i < views.length; i++) {
  289.         var view = views[i];
  290.         var commandId = "calendar_" + view.id + "_command";
  291.         var command = document.getElementById(commandId);
  292.         if (view.id == aViewType + "-view") {
  293.             command.setAttribute("checked", "true");
  294.         } else {
  295.             command.removeAttribute("checked");
  296.         }
  297.     }
  298.  
  299.     // Disable the menuitem when not in day or week view.
  300.     var rotated = document.getElementById("calendar_toggle_orientation_command");
  301.     if (aViewType == "day" || aViewType == "week") {
  302.         rotated.removeAttribute("disabled");
  303.     } else {
  304.         rotated.setAttribute("disabled", "true");
  305.     }
  306.  
  307.     try {
  308.         selectedDay = viewDeck.selectedPanel.selectedDay;
  309.         currentSelection = viewDeck.selectedPanel.getSelectedItems({});
  310.     } catch (ex) {
  311.         // This dies if no view has even been chosen this session, but that's
  312.         // ok because we'll just use now() below.
  313.     }
  314.  
  315.     if (!selectedDay) {
  316.         selectedDay = now();
  317.     }
  318.  
  319.     // Anyone wanting to plug in a view needs to follow this naming scheme
  320.     var view = document.getElementById(aViewType + "-view");
  321.     viewDeck.selectedPanel = view;
  322.  
  323.     var compositeCal = getCompositeCalendar();
  324.     if (view.displayCalendar != compositeCal) {
  325.         view.displayCalendar = compositeCal;
  326.         view.timezone = calendarDefaultTimezone();
  327.         view.controller = calendarViewController;
  328.     }
  329.  
  330.     view.goToDay(selectedDay);
  331.     view.setSelectedItems(currentSelection.length, currentSelection);
  332. }
  333.  
  334. function moveView(aNumber) {
  335.     currentView().moveView(aNumber);
  336. }
  337.  
  338. /**
  339.  * Returns the calendar view deck.
  340.  */
  341. function getViewDeck() {
  342.     return document.getElementById("view-deck");
  343. }
  344.  
  345. /**
  346.  * Returns the currently visible calendar view.
  347.  */
  348. function currentView() {
  349.     return getViewDeck().selectedPanel;
  350. }
  351.  
  352. /**
  353.  * Returns the selected day in the views in a app (Sunbird vs. Lightning)
  354.  * neutral way
  355.  */
  356. function getSelectedDay() {
  357.     return currentView().selectedDay;
  358. }
  359.  
  360. var gMidnightTimer;
  361.  
  362. /** Creates a timer that will fire after midnight.  Pass in a function as
  363.  * aRefreshCallback that should be called at that time.
  364.  */
  365. function scheduleMidnightUpdate(aRefreshCallback) {
  366.     var jsNow = new Date();
  367.     var tomorrow = new Date(jsNow.getFullYear(), jsNow.getMonth(), jsNow.getDate() + 1);
  368.     var msUntilTomorrow = tomorrow.getTime() - jsNow.getTime();
  369.  
  370.     // Is an nsITimer/callback extreme overkill here? Yes, but it's necessary to
  371.     // workaround bug 291386.  If we don't, we stand a decent chance of getting
  372.     // stuck in an infinite loop.
  373.     var udCallback = {
  374.         notify: function(timer) {
  375.             aRefreshCallback();
  376.         }
  377.     };
  378.  
  379.     if (!gMidnightTimer) {
  380.         // Observer for wake after sleep/hibernate/standby to create new timers and refresh UI
  381.         var wakeObserver = {
  382.            observe: function(aSubject, aTopic, aData) {
  383.                if (aTopic == "wake_notification") {
  384.                    aRefreshCallback();
  385.                }
  386.            }
  387.         };
  388.  
  389.         // Add observer
  390.         var observerService = Components.classes["@mozilla.org/observer-service;1"]
  391.                                         .getService(Components.interfaces.nsIObserverService);
  392.         observerService.addObserver(wakeObserver, "wake_notification", false);
  393.  
  394.         // Remove observer on unload
  395.         window.addEventListener("unload",
  396.                                 function() {
  397.                                     observerService.removeObserver(wakeObserver, "wake_notification");
  398.                                 }, false);
  399.  
  400.         gMidnightTimer = Components.classes["@mozilla.org/timer;1"]
  401.                                    .createInstance(Components.interfaces.nsITimer);
  402.     } else {
  403.         gMidnightTimer.cancel();
  404.     }
  405.     gMidnightTimer.initWithCallback(udCallback, msUntilTomorrow, gMidnightTimer.TYPE_ONE_SHOT);
  406. }
  407.  
  408. // Returns the actual style sheet object with the specified path.  Callers are
  409. // responsible for any caching they may want to do.
  410. function getStyleSheet(aStyleSheetPath) {
  411.     for each (var sheet in document.styleSheets) {
  412.         if (sheet.href == aStyleSheetPath) {
  413.             return sheet;
  414.         }
  415.     }
  416.     // Avoid the js strict "function does not always return a value" warning.
  417.     return null;
  418. }
  419.  
  420. /**
  421.  * Updates the style rules for a particular object.  If the object is a
  422.  * category (and hence doesn't have a uri), we set the category bar color.
  423.  * If it's a calendar, we set the background color and contrasting text color.
  424.  * @param aObject either a calendar (with a .uri), or the category color
  425.  * pref key suffix [the non-unicode part after "calendar.category.color.",
  426.  * equivalent to formatStringForCSSRule(categoryNameInUnicode)].
  427.  */
  428. function updateStyleSheetForObject(aObject, aSheet) {
  429.     var selectorPrefix, name, ruleUpdaterFunc;
  430.     if (aObject.uri) {
  431.         // For a calendar, set background and contrasting text colors
  432.         name = aObject.uri.spec;
  433.         selectorPrefix = "item-calendar=";
  434.         ruleUpdaterFunc = function calendarRuleFunc(aRule, aIndex) {
  435.             var color = aObject.getProperty('color');
  436.             if (!color) {
  437.                 color = "#A8C2E1";
  438.             }
  439.             aRule.style.backgroundColor = color;
  440.             aRule.style.color = getContrastingTextColor(color);
  441.         };
  442.     } else {
  443.         // For a category, set the category bar color.  Also note that
  444.         // it uses the ~= selector, since there could be multiple categories.
  445.         name = aObject;
  446.         selectorPrefix = "item-category~=";
  447.         ruleUpdaterFunc = function categoryRuleFunc(aRule, aIndex) {
  448.             var color = getPrefSafe("calendar.category.color."+name, null);
  449.             if (color) {
  450.                 aRule.style.backgroundColor = color;
  451.             } else {
  452.                 aSheet.deleteRule(aIndex);
  453.             }
  454.         };
  455.     }
  456.  
  457.     var selector = '.calendar-item[' + selectorPrefix + '"' + name + '"]';
  458.  
  459.     // Now go find our rule
  460.     var rule, ruleIndex;
  461.     for (var i = 0; i < aSheet.cssRules.length; i++) {
  462.         var maybeRule = aSheet.cssRules[i];
  463.         if (maybeRule.selectorText && (maybeRule.selectorText == selector)) {
  464.             rule = maybeRule;
  465.             ruleIndex = i;
  466.             break;
  467.         }
  468.     }
  469.  
  470.     if (!rule) {
  471.         aSheet.insertRule(selector + ' { }', aSheet.cssRules.length);
  472.         rule = aSheet.cssRules[aSheet.cssRules.length-1];
  473.     }
  474.  
  475.     ruleUpdaterFunc(rule, ruleIndex);
  476. }
  477.  
  478. /** 
  479.  *  Sets the selected day in the minimonth to the currently selected day
  480.  *  in the embedded view.
  481.  */
  482. function observeViewDaySelect(event) {
  483.     var date = event.detail;
  484.     var jsDate = new Date(date.year, date.month, date.day);
  485.  
  486.     // for the month and multiweek view find the main month,
  487.     // which is the month with the most visible days in the view;
  488.     // note, that the main date is the first day of the main month
  489.     var jsMainDate;
  490.     if (!event.originalTarget.supportsDisjointDates) {
  491.         var mainDate = null;
  492.         var maxVisibleDays = 0;
  493.         var startDay = currentView().startDay;
  494.         var endDay = currentView().endDay;
  495.         var firstMonth = startDay.startOfMonth;
  496.         var lastMonth = endDay.startOfMonth;
  497.         for (var month = firstMonth.clone(); month.compare(lastMonth) <= 0; month.month += 1) {
  498.             var visibleDays = 0;
  499.             if (month.compare(firstMonth) == 0) {
  500.                 visibleDays = startDay.endOfMonth.day - startDay.day + 1;
  501.             } else if (month.compare(lastMonth) == 0) {
  502.                 visibleDays = endDay.day;
  503.             } else {
  504.                 visibleDays = month.endOfMonth.day;
  505.             }
  506.             if (visibleDays > maxVisibleDays) {
  507.                 mainDate = month.clone();
  508.                 maxVisibleDays = visibleDays;
  509.             }
  510.         }
  511.         jsMainDate = new Date(mainDate.year, mainDate.month, mainDate.day);
  512.     }
  513.  
  514.     getMinimonth().selectDate(jsDate, jsMainDate);
  515.     currentView().focus();
  516. }
  517.  
  518. /** Provides a neutral way to get the minimonth, regardless of whether we're in
  519.  * Sunbird or Lightning.
  520.  */
  521. function getMinimonth() {
  522.     var sbMinimonth = document.getElementById("lefthandcalendar");
  523.     return sbMinimonth || document.getElementById("ltnMinimonth");
  524. }
  525.  
  526. /**
  527.  * Update the view orientation based on the checked state of the command
  528.  */
  529. function toggleOrientation() {
  530.     var cmd = document.getElementById("calendar_toggle_orientation_command");
  531.     var newValue = (cmd.getAttribute("checked") == "true" ? "false" : "true");
  532.     cmd.setAttribute("checked", newValue);
  533.  
  534.     var deck = getViewDeck();
  535.     for each (var view in deck.childNodes) {
  536.         view.rotated = (newValue == "true");
  537.     }
  538.  
  539.     // orientation refreshes automatically
  540. }
  541.  
  542. /**
  543.  * Toggle the workdays only checkbox and refresh the current view
  544.  */
  545. function toggleWorkdaysOnly() {
  546.     var cmd = document.getElementById("calendar_toggle_workdays_only_command");
  547.     var newValue = (cmd.getAttribute("checked") == "true" ? "false" : "true");
  548.     cmd.setAttribute("checked", newValue);
  549.  
  550.     var deck = getViewDeck();
  551.     for each (var view in deck.childNodes) {
  552.         view.workdaysOnly = (newValue == "true");
  553.     }
  554.  
  555.     // Refresh the current view
  556.     currentView().goToDay(currentView().selectedDay);
  557. }
  558.  
  559. /**
  560.  * Toggle the tasks in view checkbox and refresh the current view
  561.  */
  562. function toggleTasksInView() {
  563.     var cmd = document.getElementById("calendar_toggle_tasks_in_view_command");
  564.     var newValue = (cmd.getAttribute("checked") == "true" ? "false" : "true");
  565.     cmd.setAttribute("checked", newValue);
  566.  
  567.     var deck = getViewDeck();
  568.     for each (var view in deck.childNodes) {
  569.         view.tasksInView = (newValue == "true");
  570.     }
  571.  
  572.     // Refresh the current view
  573.     currentView().goToDay(currentView().selectedDay);
  574. }
  575.  
  576. /**
  577.  * Provides a neutral way to go to the current day
  578.  */
  579. function goToDate(aDate) {
  580.     getMinimonth().value = aDate.jsDate;
  581.     currentView().goToDay(aDate);
  582. }
  583.  
  584. /**
  585.  * Sets up menuitems for the views
  586.  */
  587. function initializeViews() {
  588.     // Set up the checkbox variables. Do not use the typical toggle*() functions
  589.     // because those will actually toggle the current value.
  590.     const kWorkdaysCommand = "calendar_toggle_workdays_only_command";
  591.     const kTasksInViewCommand = "calendar_toggle_tasks_in_view_command";
  592.     const kOrientation = "calendar_toggle_orientation_command";
  593.     const kHideCompleted = "hide-completed-checkbox";
  594.     var workdaysOnly = (document.getElementById(kWorkdaysCommand)
  595.                                 .getAttribute("checked") == "true");
  596.  
  597.     var tasksInView = (document.getElementById(kTasksInViewCommand)
  598.                                .getAttribute("checked") == "true");
  599.     var rotated = (document.getElementById(kOrientation)
  600.                            .getAttribute("checked") == "true");
  601.     var showCompleted = !document.getElementById(kHideCompleted).checked;
  602.  
  603.     var deck = getViewDeck();
  604.     for each (var view in deck.childNodes) {
  605.         view.workdaysOnly = workdaysOnly;
  606.         view.tasksInView = tasksInView;
  607.         view.showCompleted = showCompleted;
  608.         view.rotated = rotated;
  609.     }
  610.  
  611.     // Restore the last shown view
  612.     var deck = getViewDeck();
  613.     if (deck.selectedIndex < 0) {
  614.         // No deck item was selected beforehand, default to week view.
  615.         selectCalendarView("week");
  616.     } else {
  617.         var viewNode = deck.childNodes[deck.selectedIndex];
  618.         var viewName = viewNode.id.replace(/-view/, "");
  619.         selectCalendarView(viewName);
  620.     }
  621. }
  622.  
  623. /**
  624.  *  Deletes items currently selected in the view.
  625.  */
  626. function deleteSelectedEvents() {
  627.     var selectedItems = currentView().getSelectedItems({});
  628.     calendarViewController.deleteOccurrences(selectedItems.length,
  629.                                              selectedItems,
  630.                                              false,
  631.                                              false);
  632. }
  633.  
  634. /**
  635.  *  Edit the items currently selected in the view.
  636.  */
  637. function editSelectedEvents() {
  638.     var selectedItems = currentView().getSelectedItems({});
  639.     if (selectedItems && selectedItems.length >= 1) {
  640.         modifyEventWithDialog(getOccurrenceOrParent(selectedItems[0]));
  641.     }
  642. }
  643.  
  644. /**
  645.  * Select all events from all calendars
  646.  */
  647. function selectAllEvents() {
  648.     var items = [];
  649.     var listener = {
  650.         onOperationComplete: function selectAll_ooc(aCalendar, aStatus, 
  651.                                                     aOperationType, aId, 
  652.                                                     aDetail) {
  653.             currentView().setSelectedItems(items.length, items, false);
  654.         },
  655.         onGetResult: function selectAll_ogr(aCalendar, aStatus, aItemType, 
  656.                                             aDetail, aCount, aItems) {
  657.             for each (var item in aItems) {
  658.                 items.push(item);
  659.             }
  660.         }
  661.     };
  662.  
  663.     var composite = getCompositeCalendar();
  664.     var filter = composite.ITEM_FILTER_COMPLETED_ALL |
  665.                  composite.ITEM_FILTER_CLASS_OCCURRENCES;
  666.  
  667.     if (currentView().tasksInView) {
  668.         filter |= composite.ITEM_FILTER_TYPE_ALL; 
  669.     } else {
  670.         filter |= composite.ITEM_FILTER_TYPE_EVENT;
  671.     }
  672.  
  673.     // Need to move one day out to get all events
  674.     var end = currentView().endDay.clone();
  675.     end.day += 1;
  676.  
  677.     composite.getItems(filter, 0, currentView().startDay, end, listener);
  678. }
  679.